package me.samlss.lighter;

import me.samlss.lighter.interfaces.LighterInternalAction;
import me.samlss.lighter.interfaces.OnLighterListener;
import me.samlss.lighter.interfaces.OnLighterViewClickListener;
import me.samlss.lighter.parameter.LighterParameter;
import me.samlss.lighter.parameter.MarginOffset;
import me.samlss.lighter.shape.RectShape;
import me.samlss.lighter.util.LogUtil;
import me.samlss.lighter.util.Preconditions;
import me.samlss.lighter.util.ViewUtils;
import me.samlss.lighter.view.LighterView;
import ohos.aafwk.ability.Ability;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.app.dispatcher.TaskDispatcher;
import ohos.global.resource.NotExistException;
import ohos.global.resource.Resource;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.TouchEvent;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * @author: zhu_zhonglin
 * @time: 2021/4/25
 */
public class LighterInternalImpl implements LighterInternalAction {
    private final String TAG = getClass().getSimpleName();
    private List<List<LighterParameter>> mHighlightedParameterList = new ArrayList<>();
    private LighterView mLighterView;
    private ComponentContainer mRootView;
    private boolean isReleased = false;
    private boolean isAutoNext = true;
    private boolean isShowing = false;
    private boolean intercept = false;
    private boolean isDecorView = false;
    private boolean hasDidRootViewGlobalLayout = false;

    private int mShowIndex;
    private OnLighterListener mOnLighterListener;
    private OnLighterViewClickListener mOutSideLighterClickListener;

    private TaskDispatcher dispatcher;
    private List<LighterParameter> lighterParameters;


    /**
     * Ability 无法获取Window中的DecorView，故此方法暂时废弃，使用传入rootView的方式实现
     *
     * @param ability
     */
    public LighterInternalImpl(final Ability ability) {
        mLighterView = new LighterView(ability);
        isDecorView = true;


        Component rootView = ability.getCurrentFocus();
        Optional<Component> componentOptional = ability.getWindow().getCurrentComponentFocus();
        if (componentOptional.isPresent()) {
            mRootView = (ComponentContainer) componentOptional.get();
        }
        LogUtil.loge(TAG, "rootView:" + (rootView == null) + " mRootView:" + (mRootView == null));

        if (mRootView == null)
            return;
        mRootView.setLayoutRefreshedListener(mRootViewLayoutChangeListener);
    }

    /**
     *
     * @param rootView
     */
    public LighterInternalImpl(ComponentContainer rootView) {
        mRootView = rootView;
        mLighterView = new LighterView(rootView.getContext());
        dispatcher = rootView.getContext().getMainTaskDispatcher();
        mRootView.setLayoutRefreshedListener(mRootViewLayoutChangeListener);
    }

    /**
     *
     * @param rootView
     * @param canDrag
     */
    public LighterInternalImpl(ComponentContainer rootView, boolean canDrag) {
        mRootView = rootView;
        mLighterView = new LighterView(rootView.getContext(), canDrag);
        dispatcher = rootView.getContext().getMainTaskDispatcher();
        mRootView.setLayoutRefreshedListener(mRootViewLayoutChangeListener);
    }

    /**
     * 添加高亮显示
     * @param lighterParameters lighterParameters
     */
    public void addHighlight(LighterParameter... lighterParameters) {
        if (isReleased) {
            return;
        }
        if (lighterParameters != null
                && lighterParameters.length > 0) {
            mHighlightedParameterList.add(Arrays.asList(lighterParameters));
        }
    }

    @Override
    public boolean isShowing() {
        return isShowing;
    }

    @Override
    public boolean hasNext() {
        if (isReleased) {
            return false;
        }

        return !mHighlightedParameterList.isEmpty();
    }

    @Override
    public void next() {
        if (isReleased) {
            return;
        }

        //To ensure the root view has attached to window
        if (!ViewUtils.isAttachedToWindow(mRootView)) {
            show();
            return;
        }

        if (!hasNext()) {
            LogUtil.loge(TAG, "===release");
            dismiss();
        } else {
            isShowing = true;
            if (mOnLighterListener != null) {
                mOnLighterListener.onShow(mShowIndex);
            }

            mShowIndex++;
            lighterParameters = mHighlightedParameterList.get(0);
            for (LighterParameter lighterParameter : lighterParameters) {
                checkLighterParameter(lighterParameter);
            }

            mLighterView.setInitWidth(mRootView.getWidth() - mRootView.getPaddingLeft() - mRootView.getPaddingRight());
            mLighterView.setInitHeight(mRootView.getHeight() - mRootView.getPaddingTop() - mRootView.getPaddingBottom());

            LogUtil.loge(TAG, "===addHighlight");
            mLighterView.addHighlight(lighterParameters);
            mHighlightedParameterList.remove(0);
        }
    }

    /**
     * Check parameter.
     *
     * @param lighterParameter
     */
    private void checkLighterParameter(LighterParameter lighterParameter) {
        if (lighterParameter.getLighterShape() == null) {
            lighterParameter.setLighterShape(new RectShape());
        }

        if (lighterParameter.getHighlightedView() == null) {
            lighterParameter.setHighlightedView(mRootView.findComponentById(lighterParameter.getHighlightedViewId()));
        }

        if (lighterParameter.getTipView() == null) {
            Component component = LayoutScatter.getInstance(mLighterView.getContext()).parse(lighterParameter.getTipLayoutId(), mLighterView, false);
            lighterParameter.setTipView(component);
        }

        if (lighterParameter.getHighlightedView() == null) {
            Preconditions.checkNotNull(lighterParameter.getHighlightedView(), "Please pass a highlighted view or an id of highlighted.");
        }

        if (lighterParameter.getTipView() == null) {
            Preconditions.checkNotNull(lighterParameter.getTipView(), "Please pass a tip view or a layout id of tip view.");
        }

        if (lighterParameter.getTipViewRelativeMarginOffset() == null) {
            lighterParameter.setTipViewRelativeMarginOffset(new MarginOffset()); //use empty offset.
        }

        ViewUtils.calculateHighlightedViewRect(mLighterView, lighterParameter);
    }

    /**
     * Release all when all specified highlights are completed.
     */
    private void onRelease() {
        if (isReleased) {
            return;
        }

        isReleased = true;
        mRootView.setLayoutRefreshedListener(null);

        mRootView.removeComponent(mLighterView);
        mLighterView.removeAllComponents();

        mHighlightedParameterList.clear();
        mHighlightedParameterList = null;

        mLighterViewClickListener = null;
        mOnLighterListener = null;
        mRootView = null;
        mLighterView = null;
    }

    @Override
    public void show() {
        if (isReleased) {
            return;
        }
        LogUtil.loge(TAG, "===show===");

        if (!intercept) {
            mLighterView.setClickedListener(mLighterViewClickListener);
        }

        //To ensure the root view has attached to window
        if (ViewUtils.isAttachedToWindow(mRootView)) {
            dispatcher.delayDispatch(() -> {
                LogUtil.loge(TAG, "===add===1");
                if (mLighterView.getComponentParent() == null) {
                    LogUtil.loge(TAG, "===add===2");
                    LogUtil.loge(TAG, "mRootView: Width: " + mRootView.getWidth() + " Height:  "+ mRootView.getHeight());
                    mRootView.addComponent(mLighterView, new ComponentContainer.LayoutConfig(mRootView.getWidth(), mRootView.getHeight()));

                }
                mShowIndex = 0;
                next();
            }, 100);
        } else {
            mRootView.getComponentTreeObserver().addTreeLayoutChangedListener(mGlobalLayoutListener);
        }
    }

    @Override
    public void dismiss() {
        if (mOnLighterListener != null) {
            mOnLighterListener.onDismiss();
        }

        isShowing = false;
        onRelease();
    }

    /**
     * When you call the {@link #show()} method, maybe the {@link #mRootView} has not been initialized yet and the highlighted view property gets failed.
     * Therefore, before {@link #show()}, will invoke {@link ViewUtils#isAttachedToWindow(Component)} to check if the {@link #mRootView} has attached to window,
     * if yes, show directly, otherwise will add {@link ComponentTreeObserver#addTreeLayoutChangedListener(ComponentTreeObserver.GlobalLayoutListener)}.
     */
    private ComponentTreeObserver.GlobalLayoutListener mGlobalLayoutListener = new ComponentTreeObserver.GlobalLayoutListener() {
        @Override
        public void onGlobalLayoutUpdated() {
            /**
             * Guaranteed to be called only once.
             * */
            if (hasDidRootViewGlobalLayout) {
                return;
            }
            hasDidRootViewGlobalLayout = true;
            mRootView.getComponentTreeObserver().removeTreeLayoutChangedListener(mGlobalLayoutListener);

            show();
        }
    };


    private int left, top, right, bottom;
    private Component.LayoutRefreshedListener mRootViewLayoutChangeListener = new Component.LayoutRefreshedListener() {
        @Override
        public void onRefreshed(Component component) {
            if (left == component.getLeft() && top == component.getTop() && right == component.getRight() && bottom == component.getBottom()) {
                return;
            }
            left = component.getLeft();
            top = component.getTop();
            right = component.getRight();
            bottom = component.getBottom();
            LogUtil.loge(TAG, "left:" + left + " top:" + top + " right:" + right + " bottom:" + bottom);
            if (mLighterView == null) {
                return;
            }

            if (!isDecorView) {
                ComponentContainer.LayoutConfig layoutParams = mLighterView.getLayoutConfig();
                LogUtil.loge(TAG, "layoutParams: " + (layoutParams == null));
                layoutParams.width = Math.abs(right - left);
                layoutParams.height = Math.abs(bottom - top);

                mLighterView.setInitWidth(layoutParams.width);
                mLighterView.setInitHeight(layoutParams.height);
                mLighterView.setLayoutConfig(layoutParams);
            }

            mLighterView.reLayout();
        }

        /*@Override
        public void onLayoutChange(Component v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
            if (left == oldLeft
                    && top == oldTop
                    && right == oldRight
                    && bottom == oldBottom) {
                return;
            }
        }*/

    };

    /**
     * 设置背景颜色
     *
     * @param color
     * @param alpha
     */
    public void setBackgroundColor(int color, float alpha) {
        if (isReleased) {
            return;
        }
        //设置颜色背景
        ShapeElement element = new ShapeElement();
        element.setRgbColor(new RgbColor(color));
        int alphaInt = (int) (alpha * Element.ALPHA_MAX);
        element.setAlpha(alphaInt);
        mLighterView.setBackground(element);
    }

    private Component.ClickedListener mLighterViewClickListener = new Component.ClickedListener() {
        @Override
        public void onClick(Component v) {
            if (mOutSideLighterClickListener != null) {
                mOutSideLighterClickListener.onClick(v);
            }

            if (isAutoNext) {
                next();
            }
        }

    };

    /**
     * getLighterView
     * @return LighterView
     */
    public LighterView getLighterView() {
        return mLighterView;
    }

    /**
     * 是否自动显示下一个
     * @param autoNext autoNext
     */
    public void setAutoNext(boolean autoNext) {
        this.isAutoNext = autoNext;
    }

    /**
     * 设置是否拦截
     * @param intercept intercept
     */
    public void setIntercept(boolean intercept) {
        this.intercept = intercept;
    }

    /**
     * 设置点击事件监听
     * @param clickListener clickListener
     */
    public void setOnClickListener(OnLighterViewClickListener clickListener) {
        mOutSideLighterClickListener = clickListener;
    }

    /**
     * 设置高亮显示 回调监听
     * @param lighterListener lighterListener
     */
    public void setOnLighterListener(OnLighterListener lighterListener) {
        mOnLighterListener = lighterListener;
    }
}
